using System;
using UnityEngine;
using UnityEditor;
using Prota.Unity;
using System.Reflection;
using UnityEngine.Rendering.Universal;

namespace Prota.Editor
{
    public class EnhancedSceneView : EditorWindow
    {
        
        // ============================================================================================================
        // 配置项
        // ============================================================================================================
        
        public const GizmoType AllTypeOfGizmo = GizmoType.Active | GizmoType.Pickable | GizmoType.Selected | GizmoType.NonSelected | GizmoType.NotInSelectionHierarchy | GizmoType.InSelectionHierarchy;
        
        static EditorPrefEntryBool enabled = new("ProtaFramework.EnhancedSceneViewEnabled", false);
        static EditorPrefEntryBool showTransformConnection = new("ProtaFramework.ShowTransformConnection", false);
        static EditorPrefEntryBool showColliderRange = new("ProtaFramework.ShowColliderRange", false);
        static EditorPrefEntryBool showSpriteRendererRange = new("ProtaFramework.ShowSpriteRendererRange", false);
        static EditorPrefEntryBool showZPos = new("ProtaFramework.ShowZPos", false);
        
        [MenuItem("ProtaFramework/Functionality/Toggle Enhanced SceneView", priority = 2350)]
        static void SwitchEnhancedSceneView()
        {
            enabled.value = !enabled.value;
            Menu.SetChecked("ProtaFramework/Functionality/Toggle Enhanced SceneView", enabled.value);
        }
        
        [MenuItem("ProtaFramework/Functionality/Show Transform Connection", priority = 2351)]
        static void SwitchShowTransformConnection()
        {
            showTransformConnection.value = !showTransformConnection.value;
            Menu.SetChecked("ProtaFramework/Functionality/Show Transform Connection", showTransformConnection.value);
        }
        
        [MenuItem("ProtaFramework/Functionality/Show Collider Range", priority = 2352)]
        static void SwitchShowColliderRange()
        {
            showColliderRange.value = !showColliderRange.value;
            Menu.SetChecked("ProtaFramework/Functionality/Show Collider Range", showColliderRange.value);
        }
        
        [MenuItem("ProtaFramework/Functionality/Show SpriteRenderer Range", priority = 2353)]
        static void SwitchShowSpriteRendererRange()
        {
            showSpriteRendererRange.value = !showSpriteRendererRange.value;
            Menu.SetChecked("ProtaFramework/Functionality/Show SpriteRenderer Range", showSpriteRendererRange.value);
        }
        
        [MenuItem("ProtaFramework/Functionality/Show Z Pos", priority = 2354)]
        static void SwitchShowZPos()
        {
            showZPos.value = !showZPos.value;
            Menu.SetChecked("ProtaFramework/Functionality/Show Z Pos", showZPos.value);
            SyncShowZPos();
        }
        
        private static void SyncConfigState()
        {
            Menu.SetChecked("ProtaFramework/Functionality/Toggle Enhanced SceneView", enabled.value);
            Menu.SetChecked("ProtaFramework/Functionality/Show Transform Connection", showTransformConnection.value);
            Menu.SetChecked("ProtaFramework/Functionality/Show Collider Range", showColliderRange.value);
            Menu.SetChecked("ProtaFramework/Functionality/Show SpriteRenderer Range", showSpriteRendererRange.value);
            Menu.SetChecked("ProtaFramework/Functionality/Show Z Pos", showZPos.value);
            SyncShowZPos();
        }

        static void SyncShowZPos()
        {
            if(showZPos.value) SceneView.duringSceneGui += ShowZPos;
            else SceneView.duringSceneGui -= ShowZPos;
        }


        // ============================================================================================================
        // 功能
        // ============================================================================================================

        static EnhancedSceneView()
        {
            EditorApplication.update += Update;
        }
        
        static void Update()
        {
            foreach(var camera in SceneView.GetAllSceneCameras())
            {
                camera.clearFlags = CameraClearFlags.Color;
                camera.backgroundColor = Color.black;
            }
        }
        
        
        [DrawGizmo(AllTypeOfGizmo)]
        static void DrawTransformConnnection(Transform t, GizmoType type)
        {
            if(!enabled.value || !showTransformConnection.value) return;
            var p = t.parent;
            if(p == null) return;
            var from = p.position;
            var to = t.position;
            
            using var g = GizmosContext.Get();
            Gizmos.color = Color.green;
            Gizmos.DrawLine(from, to);
        }
        
        
        
        static readonly Color darkRed = Color.red - new Color(.3f, .3f, .3f, 0);
        static readonly Color darkGreen = Color.green - new Color(.3f, .3f, .2f, 0);
        
        [DrawGizmo(AllTypeOfGizmo)]
        static void DrawColliderRange(BoxCollider2D c, GizmoType type)
        {
            if(!enabled.value || !showColliderRange.value) return;
            if(c == null) return;
            var min = c.bounds.min;
            var max = c.bounds.max;
            using var g = GizmosContext.Get();
            Gizmos.color = c.attachedRigidbody == null ? darkRed : darkGreen;
            Gizmos.DrawLine(min, max.WithX(min.x));
            Gizmos.DrawLine(min, max.WithY(min.y));
            Gizmos.DrawLine(min.WithX(max.x), max);
            Gizmos.DrawLine(min.WithY(max.y), max);
        }
        
        
        static readonly Color darkBlue = Color.blue - new Color(.3f, .3f, .4f, 0);
        
        
        [DrawGizmo(AllTypeOfGizmo)]
        static void DrawSpriteRenderer(SpriteRenderer c, GizmoType type)
        {
            if(!enabled.value || !showSpriteRendererRange.value) return;
            if(c == null) return;
            var min = c.bounds.min;
            var max = c.bounds.max;
            min.SetZ(c.transform.position.z);
            max.SetZ(c.transform.position.z);
            using var g = GizmosContext.Get();
            Gizmos.color = darkBlue;
            Gizmos.DrawLine(min, max.WithX(min.x));
            Gizmos.DrawLine(min, max.WithY(min.y));
            Gizmos.DrawLine(min.WithX(max.x), max);
            Gizmos.DrawLine(min.WithY(max.y), max);
        }
        
        
        static GUIStyle zPosStyle;
        static void ShowZPos(SceneView view)
        {
            if(!enabled.value || !showZPos.value) return;
            zPosStyle ??= new GUIStyle(EditorStyles.label) {fontSize = 14};
            GUI.color = new Color(1f, 0.4f, 0.4f, 1f);
            
            var selects = Selection.transforms;
            if(selects.Length == 0) return;
            
            if(selects.Length == 1)
            {
                var x = HandleUtility.WorldToGUIPoint(Tools.handlePosition);
                var p = HandleUtility.GUIPointToWorldRay(x + new Vector2(20, 10)).HitXYPlane();
                Handles.Label(p, $"[z=({selects[0].position.z:F4})]", zPosStyle);
            }
            else
            {
                var min = float.MaxValue;
                var max = float.MinValue;
                foreach(var t in selects)
                {
                    min = Mathf.Min(min, t.position.z);
                    max = Mathf.Max(max, t.position.z);
                }
                
                var x = HandleUtility.WorldToGUIPoint(Tools.handlePosition);
                var p = HandleUtility.GUIPointToWorldRay(x + new Vector2(20, 10)).HitXYPlane();
                Handles.Label(p, $"[z=({min:F4})=>({max:F4}])", zPosStyle);
            }
        }
        
        
        // ====================================================================================================
        // ====================================================================================================
        // 隐藏所有自定图标的MonoBehaviour.
        
        static EditorPrefEntryBool hideProtaGizmos = new("ProtaFramework.HideProtaGizmos", false);
        [MenuItem("ProtaFramework/Functionality/Hide Prota Gizmos", priority = 2355)]
        static void SwitchHideProtaGizmos()
        {
            hideProtaGizmos.value = !hideProtaGizmos.value;
            Menu.SetChecked("ProtaFramework/Functionality/Hide Prota Gizmos", hideProtaGizmos.value);
            SetProtaFrameworkGizmosDisabled(!hideProtaGizmos.value);
        }
        
        
        // https://discussions.unity.com/t/how-to-hide-gizmos-by-script/124767/2
        static MethodInfo _methodSetIconEnabled;
        static MethodInfo methodSetIconEnabled => _methodSetIconEnabled = _methodSetIconEnabled ??
            Assembly.GetAssembly(typeof(UnityEditor.Editor))
            ?.GetType("UnityEditor.AnnotationUtility")
            ?.GetMethod("SetIconEnabled", BindingFlags.Static | BindingFlags.NonPublic);
        
        public static void SetGizmoIconEnabled(Type type, bool on)
        {
            if(methodSetIconEnabled == null) return;
            const int MONO_BEHAVIOR_CLASS_ID = 114; // https://docs.unity3d.com/Manual/ClassIDReference.html
            methodSetIconEnabled.Invoke(null, new object[] { MONO_BEHAVIOR_CLASS_ID, type.Name, on ? 1 : 0 });
        }
        
        static void SetProtaFrameworkGizmosDisabled(bool enabled)
        {
            SetGizmoIconEnabled(typeof(ProceduralMesh), enabled);
            SetGizmoIconEnabled(typeof(ERoot), enabled);
            SetGizmoIconEnabled(typeof(EComponent), enabled);
            SetGizmoIconEnabled(typeof(Parallax), enabled);
            SetGizmoIconEnabled(typeof(SpriteGroupTool), enabled);
            SetGizmoIconEnabled(typeof(DataBinding), enabled);
            SetGizmoIconEnabled(typeof(Light2D), enabled);
        }
        
        // ====================================================================================================
        // ====================================================================================================
        
        
        [InitializeOnLoadMethod]
        static void Init()
        {
            SyncConfigState();
            SetProtaFrameworkGizmosDisabled(!hideProtaGizmos.value);
        }

    }
}
